/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @file
 * @brief Interface of a simple type system for AMC.
 *
 * @author  WXQ#XDU
 * @date    2024.12
 */

#pragma once


#ifndef AMCC_TYPESYS_H
#define AMCC_TYPESYS_H

#include <stddef.h>

#include "amcc_list.h"
#include "token.h"
#include "tree.h"


AMCC_DECL_BEGIN


typedef struct data_type t_type, * t_type_ptr;

/**
 * @brief The internal type for a basic type.
 *
 * This is the type of all scalar variables and scalar parameters.
 *
 * @attention  All attributes SHOULD be accessed using API below
 *             named as "type_var_*()".
 */
typedef struct base_type {
    EnumTokenKind  baseType;
} t_basetype, t_basetype_ptr;

/**
 * @brief The internal type for an array variable or an array-parameter.
 *
 * @attention  All attributes SHOULD be accessed using API below
 *             named as "type_var_*()".
 *
 * @note In the semantic actions given in section 4.5.1 of our textbook,
 *       the complete data struct of array types is a hierarchical structure
 *       (just a tree, array of array),
 *       but in AMCC, it is a flat, single-layer structure.
 */
typedef struct array_type {

    /// base type of the array elements
    t_type_ptr  baseType;

    /// The dimension of the array and the length of each dimension.
    ///
    /// The array dimension is dimensions[0], and
    /// dimensions[k], 1<=k<=N, holds the length of dimension k.
    // ::: For a simple variable, this field is null.
    int *  dimensions;

    /// Field 'const_address' is the constant part of an element address,
    /// useless for AMC.
    ///
    /// In section 4.5.2 of our textbook, the formula 4.3 show how to
    ///    calculate this value, but this formula only applies
    ///    when each subscript starts from 1.
    ///
    /// However, in AMC, each subscript starts from 0, so
    ///
    /// (1) the formula 4.3 is useless, and const_address = 0.
    ///
    /// (2) the formula 4.6 in section 4.7.1 applies for AMC: <br/>
    ///       v<sub>1</sub> = i<sub>1</sub>, <br/>
    ///       v<sub>j</sub> = v<sub>j-1</sub> * d<sub>j</sub> +
    ///                       i<sub>j</sub>
    ///       (j=2,3, ..., n).
    ///
    /// (3) the address of element A[i<sub>1</sub>][i<sub>2</sub>]...[i<sub>n</sub>]
    ///     is  (A + v<sub>n</sub> * w).
    ///
    size_t  const_address;

} t_arrtype, * t_arrtype_ptr;


/**
 * @brief This class represents the internal type for a function.
 *
 * @attention  All attributes SHOULD be accessed using API below
 *             named as "type_func_*()".
 */
typedef struct function_type {

    t_type_ptr  returnType; //!< type of the return value

    /// "parameterTypes" keeps the order of parameters as input,
    /// The real type is t_list<t_type_ptr>.
    //
    t_list     parameterTypes;

} t_functype, * t_functype_ptr;

typedef enum enum_type_kind {
    AMCC_TYPE_ERR,      //!< flag for a bad type value
    AMCC_TYPE_BASE,     //!< flag for a base type object
    AMCC_TYPE_ARRAY,    //!< flag for an array type object
    AMCC_TYPE_FUNC      //!< flag for a function type object
} EnumTypeKind;

/**
 * @brief This class represents the type of any variable or function.
 *
 * @attention  All attributes SHOULD be accessed using API below
 *             named as "type_*()".
 */
struct  data_type {
    EnumTypeKind  typeKind;   //!< indicator for field content below
    size_t        width;      //!< memory width of each instance
    char       *  typeName;   //!< used for KEY in a set, show

    union {
        t_basetype  baseType; //!< valid if typeKind is AMCC_TYPE_BASE
        t_arrtype   arrType;  //!< valid if typeKind is AMCC_TYPE_ARRAY
        t_functype  funcType; //!< valid if typeKind is AMCC_TYPE_FUNC
    } content;
};


// -----------------------------------------------------------------------
/// @name API of class type
/// @{


/**
 * @brief Creates a type object for a basic type.
 * @param baseTypeId  token kind of basic type
 * @return Returns a type object which may be a new
 *         created object or an existing object in pool.
 *
 * @implements ONLY the type-pool can destroy a type object.
 */
extern t_type_ptr type_new_baseType(EnumTokenKind baseTypeId);

/**
 * @brief Creates a type object for a scalar or an array.
 * @param baseType  type of scalar or elements of array
 * @param dims  the dimension for an array
 * @return Returns a type object which may be a new
 *         created object or an existing object in pool.
 *
 * @implements ONLY the type-pool can destroy a type object.
 */
extern t_type_ptr type_new_varType(t_type_ptr baseType, int *dims);

/**
 * @brief Creates a type object for a function.
 * @param returnType   return type of function
 * @param paramListPtr a list of parameters with their types
 * @return Returns a type object which may be a new
 *         created object or an existing object in pool.
 *
 * @implements ONLY the type-pool can destroy a type object.
 */
extern t_type_ptr type_new_funcType(t_type_ptr returnType,
                                    t_list_ptr paramListPtr);


/**
 * @brief Get the size of a given type.
 * @param pThis  pointer to a specified type
 * @return Return the size of the specified type, or
 *         return zero for unsupported types.
 */
extern size_t type_width(t_type_ptr pThis);

/**
 * @brief Get the name of a given type.
 * @param pThis  pointer to a specified type
 * @return Return the name of the specified type.
 *    Return an empty string if argument 'pThis' is invalid.
 */
extern const char * type_name(t_type_ptr pThis);

/**
 * @brief  Get the name of a given basic type.
 * @param typeEnum  internal id of a basic type
 * @return Return the name of the specified type.
 */
extern const char * type_name_basic(EnumTokenKind typeEnum);

/**
 * @brief Get the kind of given type.
 * @param pThis pointer to a specified type
 * @return Return the kind of the specified type.
 *    Return AMCC_TYPE_ERR if argument 'pThis' is invalid.
 */
extern EnumTypeKind type_kind(t_type_ptr pThis);

/**
 * @brief Whether the type is of a function.
 * @param pThis  pointer to a specified type
 * @return Return true ONLY if the given type is of a function.
 */
extern bool   type_is_func(t_type_ptr pThis);

/**
 * @brief Whether the type is of a scalar variable.
 * @param pThis  pointer to a specified type
 * @return Return true ONLY if the given type is of a scalar variable.
 */
extern bool   type_is_scalar(t_type_ptr pThis);

/**
 * @brief Whether the type is of an array.
 * @param pThis  pointer to a specified type
 * @return Return true ONLY if the given type is of an array.
 */
extern bool   type_is_array(t_type_ptr pThis);


/**
 * @brief Get the base type of given type.
 * @param pThis  pointer to a specified type
 * @return If the given type is of an array, return the element type.
 *   If the given type is of a scalar, return its type.
 *   Otherwise, return NULL.
 */
extern t_type_ptr type_var_base(t_type_ptr pThis);

/**
 * @brief Get the basic type kind of complex type.
 * @param pThis  pointer to a specified type
 * @return If the given type is of an array, return the element type kind.
 *   If the given type is of a scalar, return its type kind.
 *   If the given type is of a function, return its return type.
 *   Otherwise, return TK_ERROR.
 */
EnumTokenKind type_var_basekind(t_type_ptr pThis);

/**
 * @brief Get the dimension of an array.
 * @param pThis  pointer to a specified type
 * @return If the given type is of an array, return its dimension.
 *         Otherwise, return 0.
 */
extern int type_var_dimcount(t_type_ptr pThis);

/**
 * @brief Get the number of element in the Kth dimension of an array.
 * @param pThis  pointer to a specified type
 * @param K     which dimension, starting from 1
 * @return Return the number of elements in the Kth dimension
 *    if the given type is of an array and K is valid,
 *    otherwise return 0.
 */
extern int type_var_dimlength(t_type_ptr pThis, int K);

/**
 * @brief Get the underlying dimension information of an array.
 * @param pThis  pointer to a specified type
 * @return If the given type is of an array, return its dimension as
 *     an integer array, and the first element is the dimension,
 *     other elements is the number of elements in each dimension.
 * @return If the given type is of an array, return NULL.
 */
extern const int * type_var_diminfo(t_type_ptr pThis);

/**
 * @brief Get the constant part in the array element memory address.
 * @param pThis  pointer to a specified type
 * @return Return the constant part if the given type is of an array,
 *   otherwise return 0.
 */
extern size_t type_var_constaddr(t_type_ptr pThis);

/**
 * @brief Get the return type of function.
 * @param pThis  pointer to a specified type
 * @return If the given type is of a function, return its return type,
 *         otherwise return NULL.
 */
extern t_type_ptr type_func_return(t_type_ptr pThis);

/**
 * @brief Get type of the Kth parameter of a function.
 * @param pThis  pointer to a specified type
 * @param K     which parameter, starting from 1
 * @return If the given type is of a function and K is valid,
 *    return the type of specified parameter,
 *    otherwise return NULL.
 * @return If a function has no any parameter, return NULL or an empty list.
 */
extern t_type_ptr type_func_paramK(t_type_ptr pThis, int K);

/**
 * @brief Get a type list of all parameters of a function.
 * @param pThis  pointer to a specified type
 * @return If the given type is of a function, return a type list
 *   of all parameters, otherwise return NULL.
 * @return If a function has no any parameter, return an empty list.
 */
extern t_list_ptr type_func_parameters(t_type_ptr pThis);


/**
 * @brief Whether one type is compatible with another type.
 *
 * If type A is compatible with type B, objects of type A can be
 * converted to objects of type B.
 *
 * If the typeTarget is an array and is a function parameter,
 * the length of first dimension is not checked.
 *
 * NOTICE: This function only support scalar or array types.
 *
 * @param typeTarget   target type
 * @param typeSource   source type
 * @param targetIsParameter true if typeTarget is of parameter
 *
 * @return Return true if typeSource is equal to typeTarget or
 *      objects of 'typeSource' can be converted to 'typeTarget'.
 */
extern bool type_is_compatible(t_type_ptr typeTarget,
                               t_type_ptr typeSource,
                               bool targetIsParameter);

/**
 * @brief Whether two types are equivalent to each other.
 *
 * @param typeTarget   target type
 * @param typeSource   source type
 * @return Return true if and ONLY if two types are exactly the same.
 */
extern bool type_is_equiv(t_type_ptr typeTarget, t_type_ptr typeSource);


/// @}
// -----------------------------------------------------------------------


// -----------------------------------------------------------------------
/// @name API of type-system
/// @{

//
// some CAPITAL names to highlighting them
//

/// Infer the type of expression
#define EXPRTYPE_INFER(op, type1, type2) type_infer(op, type1, type2)

/// Store the type of expression
#define EXPRTYPE_STORE(node, typeId)    expr_typepool_put(node, typeId)

/// Get the type of expression
#define EXPRTYPE_GET(node)              expr_typepool_get(node)


/**
 * @brief Infer the type of AMC expression.
 * @param op        the operator or ID for variable name
 * @param pType1    type of the first operand, or type of ID
 * @param pType2    type of the second operand,
 *                  null for unary operator or ID
 * @return  For a scalar or an array, return its type.
 *    For a literal, return its type.
 *    For a binary or unary expression, return the type of its value.
 *    For other cases, return type1.
 */
extern t_type_ptr  type_infer ( EnumTokenKind op  ,
                                t_type_ptr pType1 ,
                                t_type_ptr pType2 );

/**
 * @brief Infer the type of AMC expression.
 * @param op        the operator or ID for variable name
 * @param type1     type of the first operand, or type of ID
 * @param type2     type of the second operand,
 *                  TK_EOF for unary operator or ID
 * @return  For an ID, return its type.
 *    For a literal, return its type.
 *    For a binary or unary expression, return the type of its value.
 *    For other cases, return type1.
 */
extern EnumTokenKind  type_infer_enum( EnumTokenKind op ,
                                       EnumTokenKind type1 ,
                                       EnumTokenKind type2 );

/// Puts the type of expression into pool
extern void  expr_typepool_put(t_tree_ptr node, t_type_ptr type);

/// Fetches the type of expression from pool
extern t_type_ptr expr_typepool_get(t_tree_ptr node);


/// @}
// -----------------------------------------------------------------------


AMCC_DECL_END

#endif // #ifndef AMCC_TYPESYS_H
